package cn.edu.cuit.elena.transaction.system.priviledge;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import cn.edu.cuit.elena.common.Configurable;
import cn.edu.cuit.elena.common.Configuration;
import cn.edu.cuit.elena.common.SackConstants;
import cn.edu.cuit.elena.db.DataSource;
import cn.edu.cuit.elena.db.dao.BaseDao;
import cn.edu.cuit.elena.trace.Tracer;
import cn.edu.cuit.elena.transaction.system.UserContext;

public class PriviledgeManager
{
    public static final boolean GRANT_PRIVILEDGE = true;
    public static final boolean DEPRIVE_PRIVILEDGE = false;

    private static DataSource systemDataSource;
    private static BaseDao baseDao;
    private static Map<String, String> allPriviledges = new HashMap<String, String>();

    static
    {
        Configurable configurable = Configuration.create( SackConstants.SYSTEM_CONFIGURATION );
        systemDataSource = DataSource.create( configurable );
        baseDao = new BaseDao( systemDataSource );
        refreshAllPriviledges();
    }

    /**
     * Authentication and loaded user context, such as <strong>modules context</strong> etc..
     * */
    public static boolean authorize( UserContext userContext )
    {
        if( canLogin( userContext ) )
        {
            loadUserModule( userContext );

            Tracer.infoTrace( PriviledgeManager.class, userContext.getUserName() + " login.", null );

            return true;
        }
        else
        {
            Tracer.infoTrace( PriviledgeManager.class, userContext.getUserName() + " rejected.", null );
            return false;
        }
    }

    private static boolean canLogin( UserContext userContext )
    {
        String userName = userContext.getUserName();
        String password = userContext.getPassword();
        @SuppressWarnings( "unchecked" )
        Map<String, String> user = (Map<String, String>) baseDao.queryForMapOFoneRow(
            SackConstants.SQL.ELENA_USER_QUERYBYID.sql(), userName );
        if( user != null && password.equals( user.get( "password" ) ) )
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private static void loadUserModule( UserContext userContext )
    {
        String userName = userContext.getUserName();

        try
        {
            @SuppressWarnings( "unchecked" )
            List<List<String>> priviledges_list = baseDao.queryForList(
                SackConstants.SQL.ELENA_USER_PRIVILEDGE_QUERYBYUSER.sql(), Arrays.asList( userName ) );

            Map<String, String> priviledges = new HashMap<String, String>();
            for( List<String> list : priviledges_list )
            {
                priviledges.put( list.get( 0 ), list.get( 1 ) );
            }

            for( Entry<String, String> entry : priviledges.entrySet() )
            {
                Tracer.infoTrace( PriviledgeManager.class, "User : " + userContext.getUserName() + " has privilege : "
                    + entry.getKey() + ":" + entry.getValue(), null );
            }

            userContext.setModules( priviledges );
        }
        catch( Exception e )
        {
            Tracer.debugTrace( PriviledgeManager.class, "Set modules to emputy map because of exception:", e );
            userContext.setModules( new HashMap<String, String>() );
        }
    }

    /**
     * @see boolean accessLegal( UserContext userContext, int moduleCode )
     * */
    public static boolean accessLegal( UserContext userContext, String relativeUrl )
    {
        Tracer.infoTrace( PriviledgeManager.class, "url:" + relativeUrl, null );

        if( userContext == null )
        {
            Tracer
                .fatalTrace( PriviledgeManager.class, "Can't access designated URL due to UserContext is null!", null );
            return false;
        }

        Map<String, String> map = userContext.getModules();

        if( map == null )
        {
            return false;
        }
        else
        {
            String url;

            if( relativeUrl.contains( "?" ) )
            {
                int index = relativeUrl.indexOf( '?' );
                url = relativeUrl.substring( 0, index );
            }
            else
            {
                url = relativeUrl;
            }

            return map.containsValue( url );
        }
    }

    /**
     * This method matches the user modules access privilege to the <code>allPriviledge</code> field
     * which was initialized by call <code>refreshAllPriviledge</code> , rather than query it from
     * DB directly.
     * 
     * @see boolean accessLegal( UserContext userContext, String relativeUrl )
     * */
    public static boolean accessLegal( UserContext userContext, int moduleCode )
    {
        Tracer.infoTrace( PriviledgeManager.class, "module code:" + moduleCode, null );

        if( userContext == null )
        {
            Tracer
                .fatalTrace( PriviledgeManager.class, "Can't access designated URL due to UserContext is null!", null );
            return false;
        }

        Map<String, String> map = userContext.getModules();

        return map != null && map.get( String.valueOf( moduleCode ) ) != null
            && map.get( String.valueOf( moduleCode ) ).equals( allPriviledges.get( String.valueOf( moduleCode ) ) );
    }

    public static void grantPriviledgeToUser( UserContext userContext, List<String> modules, boolean flag )
    {
        if( userContext == null )
        {
            Tracer.fatalTrace( PriviledgeManager.class, "Can't grant privilege due to UserContext is null!", null );
            return;
        }

        Map<String, String> priviledges = new HashMap<String, String>();
        if( flag )
        {
            for( String module : modules )
            {
                Tracer.debugTrace( PriviledgeManager.class, "Grant to " + userContext.getUserName() + " module:"
                    + module, null );
                baseDao.insertOneRow( SackConstants.SQL.GRANT_PRIVILEDGE_BY_ID.sql(),
                    Arrays.asList( new String[] { userContext.getUserName(), module } ) );
                priviledges.put( module, allPriviledges.get( module ) );
            }

            if( userContext.getModules() == null || userContext.getModules().size() == 0 )
            {
                userContext.setModules( priviledges );
            }
            else
            {
                userContext.getModules().putAll( priviledges );
            }
        }
        else
        {
            for( String module : modules )
            {
                if( accessLegal( userContext, Integer.parseInt( module ) ) )
                {
                    Tracer.debugTrace( PriviledgeManager.class, "Deprive from " + userContext.getUserName()
                        + " module:" + module, null );
                    baseDao.del( SackConstants.SQL.DEPRIVE_PRIVILEDGE_BY_ID.sql(),
                        Arrays.asList( new String[] { userContext.getUserName(), module } ) );
                    userContext.getModules().remove( module );
                }
            }
        }
    }

    /**
     * Privilege will be persisted into DB in order of "id, name, urlname, belongto".
     * 
     * <pre>
     *  privilege.key      :  id
     *  privilege.value[0] : name
     *  privilege.value[1] : urlname
     *  privilege.value[2] : belongto
     * </pre>
     * */
    public static void createPriviledge( Map<String, String[]> priviledges )
    {
        for( Entry<String, String[]> entry : priviledges.entrySet() )
        {
            Tracer.infoTrace( PriviledgeManager.class,
                "Create priviledge : " + entry.getKey() + " -> " + entry.getValue(), null );
            String[] strings = new String[] { entry.getKey(), entry.getValue()[0], entry.getValue()[1],
                    entry.getValue()[2] };
            baseDao.insert( SackConstants.SQL.CREATE_PIVILEDGE.sql(), Arrays.asList( strings ) );
        }
    }

    /**
     * This method queries database to get all system privileges, it will be called when launching
     * system.
     * 
     **/
    public static Map<String, String> refreshAllPriviledges()
    {
        ResultSet resultSet = baseDao.query( SackConstants.SQL.ALL_PRIVILEDGE.sql() );
        Map<String, String> priviledges = new HashMap<String, String>();

        try
        {
            while( resultSet.next() )
            {
                priviledges.put( resultSet.getString( 1 ), resultSet.getString( 2 ) );
            }

            Tracer.infoTrace( PriviledgeManager.class, "Refresh all privileges ", null );
            allPriviledges = priviledges;
            return allPriviledges;
        }
        catch( SQLException e )
        {
            Tracer.fatalTrace( e.getClass(), "No priviledge was selected due to exception:", e );
            return null;
        }
    }

    /**
     * Return current privilege since last time call the <code>refreshAllRriviledges()</code>.
     * */
    public static Map<String, String> allPriviledges()
    {
        return allPriviledges;
    }

    /**
     * Privilege tree will be organized like this:
     * <p>
     * 
     * <pre>
     *  key         :       value
     *  privilegeId : name urlname belongto
     *  
     *  Tree:
     *  //todo
     *  privilegeId
     * </pre>
     * */
    public static void loadPrivilegeTree( UserContext userContext )
    {
        Map<String, String[]> map = new HashMap<String, String[]>();
        List<List<String>> list = baseDao.queryForList( SackConstants.SQL.LOAD_USER_MODULES_TREE.sql(),
            Arrays.asList( userContext.getUserName().toString() ) );
        for( List<String> row : list )
        {
            map.put( row.get( 0 ), new String[] { row.get( 1 ), row.get( 2 ), row.get( 3 ) } );
        }

        userContext.setModulesTree( map );
    }
}
